import json
from datetime import datetime, timedelta, date

from django.db import transaction
from django.db.models import Count
from requests import RequestException

from gis.admin.decorators import list_func
from gis.common import track_logging
from gis.common.django_ext.models import paginate
from gis.common.exceptions import BizException
from gis.common.retry import retry
from gis.common.timer import api as timer_api
from itpay.core.agency import (
    receipt_service,
    agency_service,
    agency_score_service,
    agency_sort_service,
)
from itpay.core.agency import transation_service
from itpay.core.agency.const import (
    PayType,
    AgencyTransactionType,
    AGENCY_BALANCE_WARNING,
    BALANCE_WARNING_MSG,
    AgencyScoreEventType,
    AgencySortScoreType,
    PayMethodType,
)
from itpay.core.conf import IMConf
from itpay.core.mch import service as mch_service
from itpay.core.open.im_service import send_im_message_to_agency
from itpay.core.recharge_order import tonglueyun_recharge_service
from itpay.core.recharge_order.cache import order_cache
from itpay.core.recharge_order.const import (
    RechargeSourceType,
    OrderStatus,
    TASK_ORDER_TIME_OUT,
    MatchMethod,
    MIN_AMOUNT_RATE,
    TASK_TRANS_ORDER,
    TASK_ORDER_CHASE_TIME_OUT,
    TASK_ORDER_AUTO_COMMENT,
)
from itpay.core.recharge_order.errors import (
    ERROR_NO_REAL_AMOUNT,
    ERROR_MCH_USER_NOT_EXISTS,
    ERROR_ORDER_NOT_EXISTS,
    ERROR_ORDER_STATUS_CONFLICT,
)
from itpay.core.recharge_order.models import Order
from itpay.core.sys import conf_service
from itpay.utils import maestro_tracker
from itpay.utils import rpc

_LOGGER = track_logging.getLogger(__name__)


def create_order(
    agency_id,
    mch_id,
    user_id,
    source,
    amount=None,
    recharge_amount=None,
    pay_type=None,
    receipt_address_id=None,
    remark=None,
    real_amount=None,
    pay_info=None,
):
    """
    创建订单
    """
    assert amount or recharge_amount
    assert agency_id > 0
    assert mch_id > 0
    assert user_id
    assert not amount or amount > 0
    assert not recharge_amount or recharge_amount > 0
    assert isinstance(source, RechargeSourceType)
    assert not pay_type or isinstance(pay_type, PayType)
    assert not receipt_address_id or receipt_address_id > 0

    if source == RechargeSourceType.IM_AUTO:
        assert receipt_address_id
        assert pay_type

    amount = amount or recharge_amount
    receipt_address = None
    # 对于im端发起的自动订单，对于相同代理相同金额只能有一个正在上分的订单
    if source in [RechargeSourceType.IM_AUTO, RechargeSourceType.OPERATION_CONSOLE]:
        m_order = Order.objects.filter(
            agency_id=agency_id,
            mch_id=mch_id,
            user_id=user_id,
            amount=amount,
            pay_type=pay_type.value,
            status=OrderStatus.CREATED.value,
        ).first()
        if m_order:
            return m_order.to_dict()

    mch = mch_service.get_mch(mch_id)
    agency = agency_service.get_agency_by_id(agency_id)

    m_order = Order(
        agency_id=agency_id,
        agency_name=agency["user_name"],
        mch_id=mch_id,
        mch_name=mch["name"],
        user_id=user_id,
        status=OrderStatus.CREATED.value,
        source=source.value,
        amount=amount,
        real_amount=real_amount or amount,
        recharge_amount=recharge_amount or amount,
        receipt_address_id=receipt_address_id,
        remark=remark,
        can_comment=conf_service.get_order_can_comment(),
    )

    if pay_info:
        m_order.pay_info_ext = json.dumps(pay_info)

    if pay_type:
        m_order.pay_type = pay_type.value

    if receipt_address_id:
        receipt_address = receipt_service.get_receipt_address(receipt_address_id)
        m_order.receipt_type = receipt_address["receipt_type"]
        m_order.receipt_method = receipt_address["receipt_method"]
        m_order.auto_arrival = receipt_address["auto_arrival"]

        # 下单收款账户信息快照
        m_order.receipt_ext = json.dumps(
            dict(
                account=receipt_address["account"],
                qr_code=receipt_address["qr_code"],
                mch_name=receipt_address["mch_name"],
                huabei_fee_rate=receipt_address["huabei_fee_rate"],
                credit_fee_rate=receipt_address["credit_fee_rate"],
                first_name=receipt_address["first_name"],
                last_name=receipt_address["last_name"],
                bank_code=receipt_address["bank_code"],
                bank_branch=receipt_address["bank_branch"],
                phone=receipt_address["phone"],
            )
        )

    assert m_order.amount >= m_order.recharge_amount

    # 保存数据库
    with transaction.atomic():
        m_order.save()
        mch_recharge_id, pay_info = rpc_create_order(m_order.id)
        assert mch_recharge_id
        m_order.mch_recharge_id = mch_recharge_id
        if not m_order.pay_info_ext:
            m_order.pay_info_ext = json.dumps(pay_info) if pay_info else None
        m_order.save()

    # 订单超时未处理取消
    timer_api.add_task(
        TASK_ORDER_TIME_OUT, m_order.id, when=datetime.now() + timedelta(minutes=30)
    )

    # 提交同略云用于自动上分匹配
    if m_order.auto_arrival:
        if m_order.pay_info_ext:
            m_order.match_method = MatchMethod.PAY_INFO.value
        else:
            m_order.match_method = MatchMethod.SPECIAL_AMOUNT.value
            # 处理自动上分订单
            _cal_special_amount(m_order, receipt_address)

        channel_order_no = rpc_send_order_to_tonglue(m_order)
        if channel_order_no:
            m_order.channel_order_no = channel_order_no
        m_order.save()

    try:
        maestro_tracker.track_maestro_user_recharge_create(
            m_order.id,
            int(datetime.strftime(m_order.created_at, "%s")),
            m_order.agency_id,
            m_order.recharge_amount,
            m_order.mch_id,
            m_order.user_id,
        )
    except Exception:
        _LOGGER.info(
            "track_maestro_user_recharge_create error: {}".format(m_order.id),
            exc_info=True,
        )

    return m_order.to_dict()


# 银行卡特殊金额自动上分, 实际支付金额修改为特殊小数点金额
def _cal_special_amount(m_order, receipt_address):
    amount = m_order.amount
    if is_auto_recharge(m_order):
        min_recharge_amount = amount - int(amount * MIN_AMOUNT_RATE) - 1
        for real_amount in range(amount - 1, min_recharge_amount, -1):
            if order_cache.add_amount_to_lock(receipt_address["account"], real_amount):
                break
        else:
            raise BizException(ERROR_NO_REAL_AMOUNT)
        m_order.real_amount = real_amount
        m_order.phone = receipt_address["phone"]


def is_auto_recharge(m_order):
    """
    是否是自助充值

    :return:
    """
    if m_order.auto_arrival:
        return True
    return False


# 提交订单第三方，用于检查匹配用户是否支付成功
def rpc_send_order_to_tonglue(m_order):
    try:
        # 提交到第三方
        receipt_ext = json.loads(m_order.receipt_ext)
        if m_order.match_method == MatchMethod.SPECIAL_AMOUNT.value:
            channel_order_no = tonglueyun_recharge_service.create_special_amount_order(
                m_order.id,
                receipt_ext["bank_code"],
                receipt_ext["account"],
                m_order.real_amount,
            )
        else:
            pay_info_ext = json.loads(m_order.pay_info_ext)
            channel_order_no = tonglueyun_recharge_service.create_pay_info_order(
                m_order.id,
                receipt_ext["bank_code"],
                receipt_ext["account"],
                m_order.real_amount,
                pay_info_ext["account_num"],
                pay_info_ext["account_holder"],
            )
        return channel_order_no
    except Exception as e:
        _LOGGER.info(
            "tonglueyun recharge service create order fail {}".format(e), exc_info=True
        )


@retry(RequestException, tries=2)
def rpc_create_order(order_id):
    m_order = _get_order(order_id)
    mch = mch_service.get_mch(m_order.mch_id, exclude_api_key=False)
    agency = agency_service.get_agency_by_id(m_order.agency_id)
    parmas = {
        "platform_recharge_id": m_order.id,
        "agency_id": m_order.agency_id,
        "agency_nickname": agency["nickname"] if agency["nickname"] else None,
        "agency_avatar": (IMConf.img_host + agency["avatar"])
        if agency["avatar"]
        else None,
        "agency_im_id": agency["im_id"],
        "user_id": m_order.user_id,
        "amount": m_order.recharge_amount,
        "can_comment": m_order.can_comment,
        "pay_type": m_order.pay_type,
    }
    resp = rpc.do_post(mch["create_order_url"], parmas, mch["api_key"])
    if resp["status"] == 2:
        raise BizException(ERROR_MCH_USER_NOT_EXISTS)

    return resp["data"]["recharge_id"], resp["data"].get("pay_info")


def get_order(order_id):
    return _get_order(order_id).to_dict()


def get_order_by_bankcard_phone_and_real_amount(phone, real_amount):
    m_order = Order.objects.filter(phone=phone, real_amount=real_amount).first()
    if not m_order:
        raise BizException(ERROR_ORDER_NOT_EXISTS)
    return m_order.to_dict()


def _get_order(order_id, with_lock=False):
    assert order_id
    query = Order.objects.filter(pk=order_id)
    if with_lock:
        query = query.select_for_update()
    m_order = query.first()
    if not m_order:
        raise BizException(ERROR_ORDER_NOT_EXISTS)
    return m_order


def get_order_by_id(order_id):
    order = _get_order(order_id)
    return order.to_dict()


def commit_order(order_id, channel_order_no=None, remark=None, need_review=False):
    """
    发起转账
    """
    tonglueyun_order_id = None
    with transaction.atomic():
        m_order = _get_order(order_id, with_lock=True)

        if not need_review:
            if OrderStatus(m_order.status) not in [
                OrderStatus.CREATED,
                OrderStatus.PENDING_REVIEW,
            ]:
                raise BizException(ERROR_ORDER_STATUS_CONFLICT)
            m_order.status = OrderStatus.COMMITTED.value
        else:
            if OrderStatus(m_order.status) != OrderStatus.CREATED:
                raise BizException(ERROR_ORDER_STATUS_CONFLICT, m_order.status)
            m_order.status = OrderStatus.PENDING_REVIEW.value
        tonglueyun_order_id = m_order.channel_order_no
        m_order.channel_order_no = channel_order_no
        m_order.remark = remark
        m_order.save()

        # 扣代理的钱
        transation_service.add_bill(
            m_order.agency_id,
            AgencyTransactionType.AGENCY_OUT_USER_RECHARGE,
            m_order.id,
            -m_order.amount,
        )

    if OrderStatus(m_order.status) == OrderStatus.COMMITTED:
        # 调用接入方上分接口
        timer_api.add_task(TASK_TRANS_ORDER, order_id)

        if is_auto_recharge(m_order):
            account = json.loads(m_order.receipt_ext).get("account")
            order_cache.del_amount_to_lock(account, m_order.real_amount)
            try:
                if tonglueyun_order_id:
                    # 主动取消同略云的金额锁定，避免短信匹配成功，同略云未匹配成功导致相同金额无法提交到同略云
                    tonglueyun_recharge_service.revoke_order(tonglueyun_order_id)
            except Exception:
                _LOGGER.info(
                    "revoke_order fail when success {}".format(order_id), exc_info=True
                )

    try:
        agency = agency_service.get_agency_by_id(m_order.agency_id)
        if agency["balance"] < AGENCY_BALANCE_WARNING:
            send_im_message_to_agency(agency["id"], BALANCE_WARNING_MSG)

    except Exception:
        _LOGGER.info("send agency message fail {}".format(agency["id"]), exc_info=True)


def need_order_review(order_id):
    order = get_order_by_id(order_id)
    agency = agency_service.get_agency_by_id(order["agency_id"])
    if agency["warning_amount"] and agency["warning_amount"] < order["amount"]:
        return True
    return False


@retry(RequestException)
def rpc_trans_order(order_id):
    """
    远程调用接入方转账上分接口
    """
    m_order = _get_order(order_id)
    params = {"recharge_id": m_order.mch_recharge_id}

    mch = mch_service.get_mch(m_order.mch_id, exclude_api_key=False)
    resp = rpc.do_post(mch["recharge_order_url"], params, mch["api_key"])
    if resp["status"] == 0:
        success_order(order_id)
        try:
            agency_score_service.update_agency_score(
                m_order.agency_id,
                AgencyScoreEventType.ORDER_RECHARGE,
                order_id,
                m_order.mch_id,
                m_order.user_id,
            )

            agency_sort_service.update_agency_score(
                m_order.agency_id,
                AgencySortScoreType.USER_RECHARGE,
                order_id,
                user_id=m_order.user_id,
                mch_id=m_order.mch_id,
            )

        except Exception:
            _LOGGER.exception("add recharge success score exception")
    else:
        fail_order(order_id)


@retry(RequestException)
def rpc_fail_order(order_id):
    """
    远程调用订单失败
    """
    m_order = _get_order(order_id)
    params = {"recharge_id": m_order.mch_recharge_id}

    mch = mch_service.get_mch(m_order.mch_id, exclude_api_key=False)
    if mch["fail_order_url"]:
        rpc.do_post(mch["fail_order_url"], params, mch["api_key"])


def success_order(order_id):
    with transaction.atomic():
        m_order = _get_order(order_id, with_lock=True)
        if m_order.status != OrderStatus.COMMITTED.value:
            raise BizException(ERROR_ORDER_STATUS_CONFLICT)

        m_order.status = OrderStatus.SUCCESS.value
        m_order.success_at = datetime.now()
        m_order.save()

    _on_order_success(m_order)

    timer_api.add_task(
        TASK_ORDER_CHASE_TIME_OUT,
        m_order.id,
        when=datetime.now() + timedelta(minutes=20),
    )
    timer_api.cancel_task(TASK_ORDER_TIME_OUT, m_order.id)
    timer_api.add_task(
        TASK_ORDER_AUTO_COMMENT, m_order.id, datetime.now() + timedelta(days=1)
    )
    try:
        rpc_notify_order_success(order_id)
    except Exception:
        _LOGGER.info(
            "notify im order success fail: {}".format(m_order.id), exc_info=True
        )

    try:
        maestro_tracker.track_maestro_user_recharge_success(
            order_id,
            int(datetime.strftime(m_order.success_at, "%s")),
            m_order.agency_id,
            m_order.recharge_amount,
            m_order.mch_id,
            m_order.user_id,
        )
    except Exception:
        _LOGGER.info(
            "track_maestro_user_recharge_success order success fail: {}".format(
                m_order.id
            ),
            exc_info=True,
        )


def _on_order_success(m_order):
    # 更新收款账户的日收款金额统计
    try:
        if m_order.receipt_address_id:
            receipt_service.update_receipt_daily_amount(
                m_order.receipt_address_id, m_order.amount
            )

    except Exception:
        _LOGGER.exception(
            "sync daily amount of receipt address fail when order success: {}".format(
                m_order.id
            )
        )


def fail_order(order_id):
    with transaction.atomic():
        m_order = _get_order(order_id, with_lock=True)
        if m_order.status != OrderStatus.COMMITTED.value:
            raise BizException(ERROR_ORDER_STATUS_CONFLICT)

        m_order.status = OrderStatus.FAIL.value
        m_order.save()

        # 返款
        transation_service.add_bill(
            m_order.agency_id,
            AgencyTransactionType.AGENCY_IN_USER_RECHARGE_FAIL_RETURN,
            m_order.id,
            m_order.amount,
        )

    try:
        rpc_fail_order(order_id)
    except Exception:
        _LOGGER.info("rpc_fail_order error {}".format(order_id), exc_info=True)

    try:
        if is_auto_recharge(m_order):
            tonglueyun_recharge_service.revoke_order(m_order.channel_order_no)
    except Exception:
        _LOGGER.info("revoke_order error {}".format(order_id), exc_info=True)


def cancel_order(order_id, remark=None):
    with transaction.atomic():
        m_order = _get_order(order_id, with_lock=True)
        if m_order.status != OrderStatus.CREATED.value:
            raise BizException(ERROR_ORDER_STATUS_CONFLICT)

        m_order.status = OrderStatus.CANCELED.value
        m_order.remark = remark
        m_order.save()

    try:
        rpc_fail_order(order_id)
    except Exception:
        _LOGGER.info("rpc_fail_order error {}".format(order_id), exc_info=True)
    timer_api.cancel_task(TASK_ORDER_TIME_OUT, m_order.id)
    if is_auto_recharge(m_order):
        tonglueyun_recharge_service.revoke_order(m_order.channel_order_no)


def expire_order(order_id):
    with transaction.atomic():
        m_order = _get_order(order_id, with_lock=True)
        if OrderStatus(m_order.status) not in [
            OrderStatus.CREATED,
            OrderStatus.PENDING_REVIEW,
        ]:
            raise BizException(ERROR_ORDER_STATUS_CONFLICT)

        if m_order.status == OrderStatus.PENDING_REVIEW:
            transation_service.add_bill(
                m_order.agency_id,
                AgencyTransactionType.AGENCY_IN_USER_RECHARGE_FAIL_RETURN,
                m_order.id,
                m_order.amount,
            )
        m_order.status = OrderStatus.TIMEOUT.value
        m_order.save()

    try:
        rpc_fail_order(order_id)
    except Exception:
        _LOGGER.info("rpc_fail_order error {}".format(order_id), exc_info=True)

    if is_auto_recharge(m_order):
        tonglueyun_recharge_service.revoke_order(m_order.channel_order_no)


def frozen_chase(order_id):
    with transaction.atomic():
        m_order = _get_order(order_id, with_lock=True)
        if OrderStatus(m_order.status) not in [OrderStatus.SUCCESS]:
            raise BizException(ERROR_ORDER_STATUS_CONFLICT)
        m_order.status = OrderStatus.CHASE_FROZEN.value
        m_order.save()


def chase_order(order_id, remark=None):
    """
    对已上分的订单追分。
    一个订单只能追分一次。由于接入方用户可能已花掉之前的上分，所以追分的数量可能会小于
    该订单充值金额。但是接入方应该尽可能最大追回以弥补损失。
    :return:
    """

    # 这里已远程调用，在本地写事务。目的是保证追分成功的前提下才对代理上分。
    # 所以需要远程接口保证多次调用时，每次都返回最终成功追分的总数。
    chased_amount = rpc_chase_order(order_id)

    if chased_amount >= 0:
        with transaction.atomic():
            m_order = _get_order(order_id, with_lock=True)
            if m_order.status != OrderStatus.SUCCESS.value:
                raise BizException(ERROR_ORDER_STATUS_CONFLICT)

            chased_amount = min(chased_amount, m_order.amount)
            m_order.status = OrderStatus.CHASED.value
            m_order.chased_amount = chased_amount
            m_order.remark = remark
            m_order.chased_at = datetime.now()
            m_order.save()

            transation_service.add_bill(
                m_order.agency_id,
                AgencyTransactionType.AGENCY_IN_CHASE,
                m_order.id,
                chased_amount,
            )

    timer_api.cancel_task(TASK_ORDER_AUTO_COMMENT, order_id)
    timer_api.cancel_task(TASK_ORDER_CHASE_TIME_OUT, order_id)

    try:
        maestro_tracker.track_maestro_user_recharge_chase(
            m_order.id,
            int(datetime.strftime(m_order.chased_at, "%s")),
            m_order.agency_id,
            m_order.chased_amount,
            m_order.mch_id,
            m_order.user_id,
        )
    except Exception:
        _LOGGER.info(
            "track_maestro_user_recharge_chase error: {}".format(m_order.id),
            exc_info=True,
        )


def review_success(order_id):
    with transaction.atomic():
        m_order = _get_order(order_id, with_lock=True)

        if OrderStatus(m_order.status) != OrderStatus.PENDING_REVIEW:
            raise BizException(ERROR_ORDER_STATUS_CONFLICT)

        m_order.status = OrderStatus.COMMITTED.value
        m_order.save()

    timer_api.add_task(TASK_TRANS_ORDER, order_id)
    if is_auto_recharge(m_order):
        account = json.loads(m_order.receipt_ext).get("account")
        order_cache.del_amount_to_lock(account, m_order.real_amount)


def review_fail(order_id):
    with transaction.atomic():
        m_order = _get_order(order_id, with_lock=True)

        if OrderStatus(m_order.status) != OrderStatus.PENDING_REVIEW:
            raise BizException(ERROR_ORDER_STATUS_CONFLICT)

        m_order.status = OrderStatus.REVIEW_FAIL.value
        m_order.save()
        transation_service.add_bill(
            m_order.agency_id,
            AgencyTransactionType.AGENCY_IN_USER_RECHARGE_FAIL_RETURN,
            m_order.id,
            m_order.amount,
        )


@retry(RequestException)
def rpc_chase_order(order_id):
    """
    远程调用接入方追分接口
    """
    m_order = _get_order(order_id)
    params = {"recharge_id": m_order.mch_recharge_id}

    mch = mch_service.get_mch(m_order.mch_id, exclude_api_key=False)
    resp = rpc.do_post(mch["chase_order_url"], params, mch["api_key"])
    if resp["status"] == 0:
        try:
            agency_score_service.update_agency_score(
                m_order.agency_id,
                AgencyScoreEventType.ORDER_CHASE,
                order_id,
                m_order.mch_id,
                m_order.user_id,
            )
        except Exception:
            _LOGGER.exception("add chase_order score exception")
        return resp["data"]["chased_amount"]
    else:
        return 0


@retry(RequestException)
def rpc_notify_order_success(order_id):
    """
    远程调用 通知im 充值成功
    """
    m_order = _get_order(order_id)
    if m_order.can_comment:
        message = (
            """您充值的【{}】元已到账，请查收。 \n\n感谢您的支持，您可前往-〉充值记录-〉待评价订单对我的服务进行评价，麻烦亲给个好评哦～"""
        )
    else:
        message = """您充值的【{}】元已到账，请查收。"""

    params = {
        "app_id": IMConf.app_id,
        "order_id": order_id,
        "play_mch_id": m_order.mch_id,
        "player_id": int(m_order.user_id),
        "agency_id": m_order.agency_id,
        "amount": m_order.amount,
        "real_amount": m_order.real_amount,
        "message": message.format(m_order.amount / 100),
    }
    _LOGGER.info("rpc_notify_order_success params {}".format(params))
    rpc.do_post(IMConf.host + "open/pay/orders/notify/", params, IMConf.api_key)


@list_func
def list_orders(
    page_no,
    page_size,
    order_id=None,
    agency_id=None,
    agency_name=None,
    parent_id=None,
    parent_name=None,
    mch_id=None,
    user_id=None,
    mch_recharge_id=None,
    status=None,
    source=None,
    receipt_address_id=None,
    pay_method=None,
    amount_gte=None,
    amount_lte=None,
    created_at_begin=None,
    created_at_end=None,
    updated_at_begin=None,
    updated_at_end=None,
    success_at_begin=None,
    success_at_end=None,
    pay_type=None,
    order_by="-created_at",
    mch_type=None,
):
    query = Order.objects.all()
    if order_id:
        query = query.filter(pk=order_id)
    if agency_id:
        query = query.filter(agency_id=agency_id)
    if agency_name:
        query = query.filter(agency_name__contains=agency_name)
    if parent_id:
        query = query.filter(agency__parent_id=parent_id)
    if parent_name:
        query = query.filter(agency__parent__user_name__contains=parent_name)
    if mch_id:
        query = query.filter(mch_id=mch_id)
    if mch_type:
        query = query.filter(mch__mch_type=mch_type.value)
    if user_id:
        query = query.filter(user_id=user_id)
    if pay_method:
        if not isinstance(pay_method, PayMethodType):
            pay_method = PayMethodType(pay_method)
        query = query.filter(pay_method=pay_method.value)
    if pay_type:
        if not isinstance(pay_type, PayType):
            pay_type = PayType(pay_type)
        query = query.filter(pay_type=pay_type.value)
    if mch_recharge_id:
        query = query.filter(mch_recharge_id=mch_recharge_id)
    if receipt_address_id:
        query = query.filter(receipt_address_id=receipt_address_id)
    if status:
        if isinstance(status, list):
            query = query.filter(
                status__in=[
                    e.value if isinstance(e, OrderStatus) else e for e in status
                ]
            )
        else:
            query = query.filter(
                status=status.value if isinstance(status, OrderStatus) else status
            )
    if source:
        if not isinstance(source, RechargeSourceType):
            source = RechargeSourceType(source)
        query = query.filter(source=source.value)
    if amount_gte:
        query = query.filter(amount__gte=amount_gte)
    if amount_lte:
        query = query.filter(amount__lte=amount_lte)
    if created_at_begin:
        query = query.filter(created_at__gte=created_at_begin)
    if created_at_end:
        query = query.filter(created_at__lte=created_at_end)
    if updated_at_begin:
        query = query.filter(updated_at__gte=updated_at_begin)
    if updated_at_end:
        query = query.filter(updated_at__lte=updated_at_end)
    if success_at_begin:
        query = query.filter(success_at__gte=success_at_begin)
    if success_at_end:
        query = query.filter(success_at__lte=success_at_end)
    if order_by:
        query = query.order_by(order_by)

    return query.count(), [e.to_dict() for e in paginate(query, page_no, page_size)]


# 统计订单数量
def get_order_count(mch_id, user_id, agency_id=None, order_status=None):
    query = Order.objects.filter(mch_id=mch_id, user_id=user_id)
    if agency_id:
        query = query.filter(agency_id=agency_id)
    if order_status:
        assert isinstance(order_status, OrderStatus)
        query = query.filter(status=order_status.value)
    return query.count()


class TransOrderHandler(timer_api.TaskHandler):
    def handle(self, task):
        order_id = int(task.biz_num)
        try:
            rpc_trans_order(order_id)
        except BizException as e:
            _LOGGER.info(
                "trans order conflict, order_id: {}, cause: {}".format(order_id, e)
            )
        except Exception:
            # 遇到可恢复的错误时（一般是网络暂时不可用, 代码逻辑错误），一分钟后再次提交
            return datetime.now() + timedelta(minutes=1)

    def get_biz_code(self):
        return TASK_TRANS_ORDER


class OrderTimeOutHandler(timer_api.TaskHandler):
    def handle(self, task):
        order_id = int(task.biz_num)
        try:
            expire_order(order_id)
        except BizException:
            _LOGGER.info(
                "order auto time out biz exception, order_id: {}".format(order_id),
                exc_info=True,
            )
        except Exception:
            _LOGGER.exception(
                "order auto time out unknown exception order_id: {}".format(order_id)
            )

    def get_biz_code(self):
        return TASK_ORDER_TIME_OUT


class OrderChaseFrozenHandler(timer_api.TaskHandler):
    def handle(self, task):
        order_id = int(task.biz_num)
        try:
            frozen_chase(order_id)
        except BizException:
            _LOGGER.info(
                "order auto frozen biz exception, order_id: {}".format(order_id),
                exc_info=True,
            )
        except Exception:
            _LOGGER.exception(
                "order auto frozen unknown exception order_id: {}".format(order_id)
            )

    def get_biz_code(self):
        return TASK_ORDER_CHASE_TIME_OUT


def get_month_recharge(agency_id):
    today = date.today()
    first_day = datetime(year=today.year, month=today.month, day=1)
    result = Order.objects.filter(
        agency_id=agency_id,
        status__in=[
            OrderStatus.SUCCESS.value,
            OrderStatus.CHASE_FROZEN.value,
            OrderStatus.CHASED.value,
        ],
        updated_at__gte=first_day,
    ).aggregate(amount_count=Count("*"))
    return result["amount_count"]


def had_recharge_this_week(agency_id, mch_id, user_id, this_week_monday):
    last_week_monday = this_week_monday + timedelta(days=7)
    return Order.objects.filter(
        agency_id=agency_id,
        mch_id=mch_id,
        user_id=user_id,
        success_at__gt=this_week_monday,
        success_at__lt=last_week_monday,
        status__in=[
            OrderStatus.SUCCESS.value,
            OrderStatus.CHASE_FROZEN.value,
            OrderStatus.CHASED.value,
        ],
    ).exists()
